/**
 * \file Quaternion.h
 * \brief Quaternion definition class
 * \author Edouard ROGE
 * \date 20 decembre 2010
 */

#ifndef _QUATERNION_
#define _QUATERNION_

#include "Core/Memory/HeapObject.h"

namespace Math
{
	namespace M3D
	{
		template< class T > class Matrix;
		template< class T > class Vector3D;
		template< class T > class Point3D;

		/**
		 * \class Quaternion
		 * \brief class Quaternion
		 */
		class Quaternion : public Core::Memory::HeapObject
		{
		public:
			/**
			 * \union union
			 * \brief union of 3 values (x y z and w) and a array with 4 float
			 */
			union
			{
				/**
				 * \struct struct
				 * \brief x y z and w
				 */
				struct
				{
					/** x */
					float x;
					/** y */
					float y;
					/** z */
					float z;
					/** w */
					float w;
				};

				/** array of 4 float*/
				float values[4];
			};

			/**
			 * \fn Quaternion();
			 * \brief Default constructor
			 */
			Quaternion();

			/**
			 * \fn Quaternion(float _x, float _y, float _z, float _w);
			 * \param _x copy _x in x
			 * \param _y copy _y in y
			 * \param _z copy _z in z
			 * \param _w copy _w in w
			 * \brief Constructor with param
			 */
			Quaternion(float _x, float _y, float _z, float _w);

			/**
			 * \fn void Quaternion( const Matrix<float> & _mMatrix );
			 * \param _mMatrix the quaternion is created from _mMatrix Matrix
			 * \brief Constructor with param
			 */
			Quaternion(const Matrix<float> & _mMatrix);

			/**
			 * \fn Quaternion(const Vector3D<float> & _fAxis, float _fAngle);
			 * \param _fAxis rotation axis
			 * \param _fAngle angle
			 * \brief Constructor with param
			 */
			Quaternion(const Vector3D<float> & _fAxis, float _fAngle);

			/**
			 * \fn ~Quaternion();
			 * \brief Destructor
			 */
			~Quaternion();

			/**
			 * \fn void Set(float _x, float _y, float _z, float _w);
			 * \param _x copy _x in x
			 * \param _y copy _y in y
			 * \param _z copy _z in z
			 * \param _w copy _w in w
			 * \brief Setter
			 */
			void Set(float _x, float _y, float _z, float _w);

			/**
			 * \fn void CreateFromMatrix( const Matrix<float> & _mMatrix );
			 * \param _mMatrix the quaternion is created from _mMatrix Matrix
			 * \brief Create Quaternion from Matrix
			 */
			void CreateFromMatrix(const Matrix<float> & _mMatrix);

			/**
			 * \fn void CreateFromAxis( const Vector3D<float> & _fAxis, float _fAngle );
			 * \param _fAxis rotation axis
			 * \param _fAngle angle
			 * \brief Create Quaternion from axis and angle
			 */
			void CreateFromAxis(const Vector3D<float> & _fAxis, float _fAngle);

			/**
			 * \fn float GetLength() const;
			 * \return Length of the quaternion
			 * \brief get the length of the quaternion
			 */
			float GetLength() const;

			/**
			 * \fn Quaternion Normalize() const;
			 * \return the current Quaternion normalized
			 * \brief normalize quaternion
			 */
			Quaternion Normalize() const;

			/**
			 * \fn Quaternion Conjugate() const;
			 * \return a the conjugate of current Quaternion
			 * \brief get the conjugate of the quaternion
			 */
			Quaternion Conjugate() const;

			/**
			 * \fn void GetMatrix( Matrix<float> & _Matrix ) const;
			 * \param _Matrix fill the matrix corresponding to the quaternion
			 * \brief compute the matrix corresponding to the quaterion
			 */
			void GetMatrix(Matrix<float> & _Matrix) const;

			/**
			 * \fn void GetAxis( Vector3D<float> & _fAxis, float & _fAngle ) const;
			 * \param _fAxis get the rotation axis corresponding to the quaternion
			 * \param _fAngle get the rotation angle corresponding to the quaternion
			 * \brief compute the rotation corresponding to the quaternion
			 */
			void GetAxis(Vector3D<float> & _fAxis, float & _fAngle) const;

			/**
			 * \fn operator float * ();
			 * \brief cast operator
			 */
			operator float * ();

			/**
			 * \fn operator const float * ()const;
			 * \brief cast operator
			 */
			operator const float * ()const;

			/**
			 * \fn Quaternion operator - () const;
			 * \return conjugate of current quaternion
			 * \brief operator-
			 */
			Quaternion operator - () const;

			/**
			 * \fn Quaternion & operator *= (float _f);
			 * \param _f scale quaternion by _f
			 * \return (*this) scaled
			 * \brief operator *
			 */
			Quaternion & operator *= (float _f);

			/**
			 * \fn Quaternion & operator /= (float _f);
			 * \param _f divide quaternion by _f
			 * \return (*this) divided
			 * \brief operator /
			 */
			Quaternion & operator /= (float _f);

			/**
			 * \fn Quaternion & operator += (const Quaternion & _q);
			 * \param _q quaternion added to (*this)
			 * \return (*this)
			 * \brief operator +=
			 */
			Quaternion & operator += (const Quaternion & _q);

			/**
			 * \fn Quaternion & operator -= (const Quaternion & _q);
			 * \param _q subtract quaternion _q to (*this)
			 * \return (*this)
			 * \brief operator -=
			 */
			Quaternion & operator -= (const Quaternion & _q);

			/**
			 * \fn Quaternion & operator *= (const Quaternion & _q);
			 * \param _q multiply _q to (*this)
			 * \return (*this)
			 * \brief operator *=
			 */
			Quaternion & operator *= (const Quaternion & _q);

			/**
			 * \fn Quaternion & operator /= (const Quaternion & _q);
			 * \param _q divide (*this) by _q
			 * \return (*this)
			 * \brief operator /=
			 */
			Quaternion & operator /= (const Quaternion & _q);

			/**
			 * \fn friend Quaternion operator * ( const Quaternion & _q, float _f );
			 * \param _q quaternion to multiply
			 * \param _f factor size
			 * \return _q scaled by _f
			 * \brief operator *
			 */
			friend Quaternion operator * (const Quaternion & _q, float _f);

			/**
			 * \fn friend Quaternion operator / ( const Quaternion & _q, float _f );
			 * \param _q quaternion to divide
			 * \param _f factor size
			 * \return _q divided by _f
			 * \brief operator /
			 */
			friend Quaternion operator / (const Quaternion & _q, float _f);

			/**
			 * \fn friend Quaternion operator + ( const Quaternion & _q1, const Quaternion & _q2 );
			 * \param _q1 quaternion
			 * \param _q2 quaternion
			 * \return _q1 added to q2
			 * \brief operator +
			 */
			friend Quaternion operator + (const Quaternion & _q1, const Quaternion & _q2);

			/**
			 * \fn friend Quaternion operator - ( const Quaternion & _q1, const Quaternion & _q2 );
			 * \param _q1 quaternion
			 * \param _q2 quaternion
			 * \return _q1 subtracted to q2
			 * \brief operator -
			 */
			friend Quaternion operator - (const Quaternion & _q1, const Quaternion & _q2);

			/**
			 * \fn friend Quaternion operator * ( const Quaternion & _q1, const Quaternion & _q2 );
			 * \param _q1 quaternion
			 * \param _q2 quaternion
			 * \return _q1 * _q2
			 * \brief operator *
			 */
			friend Quaternion operator * (const Quaternion & _q1, const Quaternion & _q2);

			/**
			 * \fn friend Quaternion operator / ( const Quaternion & _q1, const Quaternion & _q2 );
			 * \param _q1 quaternion
			 * \param _q2 quaternion
			 * \return _q1 / _q2
			 * \brief operator /
			 */
			friend Quaternion operator / (const Quaternion & _q1, const Quaternion & _q2);
		};
	}
}

#endif // _QUATERNION_
